package beautiful.butterfly.drds.datasource.connection_contexts.spi;

import beautiful.butterfly.drds.datasource.connection_contexts.api.IParameterizedISql;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.sql.*;

public class ParameterizedSql extends Sql implements IParameterizedISql {

    public static final Logger logger = LoggerFactory.getLogger(ParameterizedSql.class);
    private final PreparedStatement preparedStatement;
    //
    private int defaultMaxFieldSize;
    private int defaultMaxRows;
    private int defaultQueryTimeout;
    private int defaultFetchDirection;
    private int defaultFetchSize;
    //
    private int maxFieldSize;
    private int maxRows;
    private int queryTimeout;
    private int fetchDirection;
    private int fetchSize;

    public ParameterizedSql(ConnectionContext connectionContext, PreparedStatement preparedStatement) throws SQLException {
        super(connectionContext, preparedStatement);
        this.preparedStatement = preparedStatement;
        //
        this.defaultQueryTimeout = this.preparedStatement.getQueryTimeout();
        this.defaultFetchDirection = this.preparedStatement.getFetchDirection();
        this.defaultFetchSize = this.preparedStatement.getFetchSize();
        this.defaultMaxRows = this.preparedStatement.getMaxRows();
        this.defaultMaxFieldSize = this.preparedStatement.getMaxFieldSize();
        //
        this.queryTimeout = this.defaultQueryTimeout;
        this.fetchDirection = this.defaultFetchDirection;
        this.fetchSize = this.defaultFetchSize;
        this.maxRows = this.defaultMaxRows;
        this.maxFieldSize = this.defaultMaxFieldSize;
    }

    public PreparedStatement getPreparedStatement() {
        return preparedStatement;
    }

    //
    public void setQueryTimeout(int queryTimeout) throws SQLException {
        this.queryTimeout = queryTimeout;
        super.setQueryTimeout(queryTimeout);
    }

    public void setFetchDirection(int fetchDirection) throws SQLException {
        this.fetchDirection = fetchDirection;
        super.setFetchDirection(fetchDirection);
    }

    public void setFetchSize(int fetchSize) throws SQLException {
        this.fetchSize = fetchSize;
        super.setFetchSize(fetchSize);
    }

    public void setMaxRows(int maxRows) throws SQLException {
        this.maxRows = maxRows;
        super.setMaxRows(maxRows);
    }

    public void setMaxFieldSize(int maxFieldSize) throws SQLException {
        this.maxFieldSize = maxFieldSize;
        super.setMaxFieldSize(maxFieldSize);
    }

    //
    public void addBatch() throws SQLException {
        checkOpen();

        try {
            this.preparedStatement.addBatch();
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    public int[] executeBatch() throws SQLException {
        checkOpen();

        this.connectionContext.beforeExecute();
        try {
            return this.preparedStatement.executeBatch();
        } catch (Exception t) {
            throw handleException(t);
        } finally {
            this.connectionContext.afterExecute();
        }
    }

    //
    public void setNull(int parameterIndex, int sqlType) throws SQLException {
        checkOpen();

        try {
            this.preparedStatement.setNull(parameterIndex, sqlType);
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    public void setBoolean(int parameterIndex, boolean x) throws SQLException {
        checkOpen();

        try {
            this.preparedStatement.setBoolean(parameterIndex, x);
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    public void setShort(int parameterIndex, short x) throws SQLException {
        checkOpen();

        try {
            this.preparedStatement.setShort(parameterIndex, x);
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    public void setInt(int parameterIndex, int x) throws SQLException {
        checkOpen();

        try {
            this.preparedStatement.setInt(parameterIndex, x);
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    public void setLong(int parameterIndex, long x) throws SQLException {
        checkOpen();

        try {
            this.preparedStatement.setLong(parameterIndex, x);
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    public void setFloat(int parameterIndex, float x) throws SQLException {
        checkOpen();

        try {
            this.preparedStatement.setFloat(parameterIndex, x);
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    public void setDouble(int parameterIndex, double x) throws SQLException {
        checkOpen();

        try {
            this.preparedStatement.setDouble(parameterIndex, x);
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    public void setString(int parameterIndex, String x) throws SQLException {
        checkOpen();

        try {
            this.preparedStatement.setString(parameterIndex, x);
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException {
        checkOpen();

        try {
            this.preparedStatement.setBigDecimal(parameterIndex, x);
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    public void setDate(int parameterIndex, Date x) throws SQLException {
        checkOpen();

        try {
            this.preparedStatement.setDate(parameterIndex, x);
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    public void setTime(int parameterIndex, Time x) throws SQLException {
        checkOpen();

        try {
            this.preparedStatement.setTime(parameterIndex, x);
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException {
        checkOpen();

        try {
            this.preparedStatement.setTimestamp(parameterIndex, x);
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    public void setObject(int parameterIndex, Object x) throws SQLException {
        checkOpen();

        try {
            this.preparedStatement.setObject(parameterIndex, x);
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    public ParameterMetaData getParameterMetaData() throws SQLException {
        checkOpen();

        try {
            return this.preparedStatement.getParameterMetaData();
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    public void clearParameters() throws SQLException {
        checkOpen();

        try {
            this.preparedStatement.clearParameters();
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    //
    public int executeUpdate() throws SQLException {
        checkOpen();

        this.connectionContext.beforeExecute();
        try {
            return this.preparedStatement.executeUpdate();
        } catch (Exception t) {
            throw handleException(t);
        } finally {
            this.connectionContext.afterExecute();
        }
    }

    public RowDataMapList executeQuery() throws SQLException {
        checkOpen();

        this.connectionContext.beforeExecute();
        try {
            ResultSet resultSet = this.preparedStatement.executeQuery();
            RowDataMapList rowDataMapList = new RowDataMapList(this, resultSet);
            bind(rowDataMapList);
            return rowDataMapList;
        } catch (Exception t) {
            throw handleException(t);
        } finally {
            this.connectionContext.afterExecute();
        }
    }

    //
    public ResultSetMetaData getResultSetMetaData() throws SQLException {
        checkOpen();

        try {
            return this.preparedStatement.getMetaData();
        } catch (Exception t) {
            throw handleException(t);
        }
    }

    //
    @Override
    public void close() throws SQLException {
        if (isClosed()) {
            return;
        }

        boolean closed = this.connectionContext.isClosed();
        if (!closed) {
            try {
                if (this.defaultQueryTimeout != this.queryTimeout) {
                    this.preparedStatement.setQueryTimeout(this.defaultQueryTimeout);
                    this.queryTimeout = this.defaultQueryTimeout;
                }
                if (this.defaultFetchDirection != this.fetchDirection) {
                    this.preparedStatement.setFetchDirection(this.defaultFetchDirection);
                    this.fetchDirection = this.defaultFetchDirection;
                }
                if (this.defaultFetchSize != this.fetchSize) {
                    this.preparedStatement.setFetchSize(this.defaultFetchSize);
                    this.fetchSize = this.defaultFetchSize;
                }
                if (this.defaultMaxRows != this.maxRows) {
                    this.preparedStatement.setMaxRows(this.defaultMaxRows);
                    this.maxRows = this.defaultMaxRows;
                }
                if (this.defaultMaxFieldSize != this.maxFieldSize) {
                    this.preparedStatement.setMaxFieldSize(this.defaultMaxFieldSize);
                    this.maxFieldSize = this.defaultMaxFieldSize;
                }
                this.preparedStatement.clearParameters();
                super.close();
            } catch (Exception e) {
                this.connectionContext.handleException(e);
            }
        }
    }
}
